#include <iostream>
#include "utf8_output.h"
#include <functional>
#include <string>
// cpp11
// template <typename F, typename Arg>
// auto Func(F f, Arg arg) -> decltype(f(arg))
// {
//     return f(arg);
// }

// cpp14
template <typename F, typename Arg>
auto Func(F f, Arg arg)
{
    return f(arg);
}
/*
std::invoke_result 是一个类型特征（type trait），它在编译期就能确定返回类型，不需要看函数体
可以在函数体外部使用
可以用于元编程
SFINAE 友好（可以用在模板特化和重载解析中）
auto 需要看到函数体才能推导返回类型
更依赖于具体实现
不能在函数体外部使用
在某些模板元编程场景下可能不够灵活
例如，这种情况下 std::invoke_result 更有用：
// 可以在函数外使用 invoke_result 查询返回类型
template<typename F, typename Arg>
using result_type = typename std::invoke_result<F, Arg>::type;

// 基于返回类型做 SFINAE
template<typename F, typename Arg,
         typename = std::enable_if_t<std::is_same_v<result_type<F, Arg>, int>>>
void foo() { }
*/

class A
{
public:
    A() = delete; // 删除构造函数，防止创建A类型的对象

public:
    int operator()(int i) // 重载()运算符，使A类型的对象可以像函数一样被调用
    {
        return i;
    }
};
// 测试用例

// 测试用的类，包含多个重载的调用运算符
struct Test
{
    int operator()(int x)
    {
        std::cout << "调用 int 版本" << std::endl;
        return x + 1;
    }

    double operator()(double x)
    {
        std::cout << "调用 double 版本" << std::endl;
        return x + 0.5;
    }

    std::string operator()(const char *x)
    {
        std::cout << "调用 string 版本" << std::endl;
        return std::string(x) + "!";
    }
};

// 使用 auto 的版本
template <typename F, typename Arg>
auto Func1(F f, Arg arg)
{
    return f(arg);
}

// 使用 invoke_result 的版本
template <typename F, typename Arg>
typename std::invoke_result<F, Arg>::type Func2(F f, Arg arg)
{
    return f(arg);
}
int add(int a, int b)
{
    return a + b;
}
int main()
{
    // 测试一个计算平方的 lambda 函数
    auto square = [](double x)
    { return x * x; };
    auto result = Func(square, 5); // 应该返回 25

    Test t;

    // 测试不同类型的调用
    std::cout << "=== 使用 auto (Func1) ===" << std::endl;
    auto a1 = Func1(t, 42);
    auto b1 = Func1(t, 3.14);
    auto c1 = Func1(t, "Hello");

    std::cout << "a1 = " << a1 << " (类型: " << typeid(a1).name() << ")" << std::endl;
    std::cout << "b1 = " << b1 << " (类型: " << typeid(b1).name() << ")" << std::endl;
    std::cout << "c1 = " << c1 << " (类型: " << typeid(c1).name() << ")" << std::endl;

    std::cout << "\n=== 使用 invoke_result (Func2) ===" << std::endl;
    auto a2 = Func2(t, 42);
    auto b2 = Func2(t, 3.14);
    auto c2 = Func2(t, "Hello");

    std::cout << "a2 = " << a2 << " (类型: " << typeid(a2).name() << ")" << std::endl;
    std::cout << "b2 = " << b2 << " (类型: " << typeid(b2).name() << ")" << std::endl;
    std::cout << "c2 = " << c2 << " (类型: " << typeid(c2).name() << ")" << std::endl;

    // 编译期类型检查
    static_assert(std::is_same<decltype(a1), int>::value, "a1 应该是 int 类型");
    static_assert(std::is_same<decltype(b1), double>::value, "b1 应该是 double 类型");
    static_assert(std::is_same<decltype(c1), std::string>::value, "c1 应该是 string 类型");

    static_assert(std::is_same<std::invoke_result<Test, int>::type, int>::value,
                  "Test(int) 应该返回 int");
    static_assert(std::is_same<std::invoke_result<Test, double>::type, double>::value,
                  "Test(double) 应该返回 double");
    // 可以通过invoke_result_t 获得函数的返回值类型
    std::invoke_result_t<Test, int> uu;

    // 1. 用于普通函数
    std::invoke_result_t<decltype(&add), int, int> result1; // 类型为 int

    // 2. 用于 lambda 表达式
    auto lambda = [](int x)
    { return x * 2.0; };
    std::invoke_result_t<decltype(lambda), int> result2; // 类型为 double

    // 3. 用于函数对象（你的 Test 类）
    std::invoke_result_t<Test, int> result3; // 类型为 int

    // 4. 用于 std::function
    std::function<std::string(int)> func = [](int x)
    { return std::to_string(x); };
    std::invoke_result_t<decltype(func), int> result4; // 类型为 std::string

    // 由于是放在模板上的可调用对象 所以 出来真的类 都需要加一个decltype

    return 0;
}
